package org.example.onlineEducation.config;

import org.example.onlineEducation.entity.Permission;
import org.example.onlineEducation.entity.Role;
import org.example.onlineEducation.filter.JwtTokenAuthenticationFilter;
import org.example.onlineEducation.service.PermissionService;
import org.example.onlineEducation.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.OncePerRequestFilter;

import java.util.*;
import java.util.stream.Collectors;

/**
 * create by xiuzhong.li at 2022/11/2
 *
 * @Description:文件描述：
 */

@Configuration
@EnableWebSecurity    // 添加 security 过滤器
@EnableGlobalMethodSecurity(prePostEnabled = true)	// 启用方法级别的权限认证
public class SecurityConfig {
    @Autowired
    private AuthenticationConfiguration authenticationConfiguration;
    @Autowired
    private UserDetailsService userDetailsServiceImpl;
    @Autowired
    private OncePerRequestFilter jwtTokenAuthenticationFilter;
    @Autowired
    private RoleService roleService;
    @Autowired
    private PermissionService permissionService;


    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 获取AuthenticationManager（认证管理器），登录时认证使用
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationManager authenticationManager() throws Exception{
        return authenticationConfiguration.getAuthenticationManager();
    }
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http)throws Exception{

        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests()
                .antMatchers("/user/login").anonymous();

        //查出数据库中所有角色
        List<Role> roles = roleService.selectAll();

        Set<Permission> set = new HashSet<>();

        roles.forEach(role -> {
            //根据角色ID查出权限路径
            List<Permission> list = permissionService.selectAllByRoleId(role.getId());

            set.addAll(list);
        });

        set.forEach(permission -> {
            System.out.println("\thref: " + permission.getHref() + "\t\troleIds: " + permission.getRoleIds());
            //将每个角色所对应的路径绑定权限：角色ID
            Set<String> roleIds = permission.getRoleIds();
            registry.antMatchers(permission.getHref()).hasAnyAuthority(roleIds.toArray(new String[roleIds.size()]));
        });

        //  基于 token，不需要 csrf
        // 基于 token，不需要 session
        //  允许/api/user/login匿名访问
        return registry
                .anyRequest().authenticated()
                .and()
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .addFilterBefore(jwtTokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }


}
